﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;

namespace intelliSys.UI.Interaction.MouseGesture
{

    /// <summary>
    /// This gesture is detected using a re-enterable timer.
    /// <para>Shake is defined as follow:</para>
    /// <para>*Mouse left button is pressed during detection</para>
    /// <para>*Mouse go back and forth for a specified rounds</para>
    /// <para>The direction supported is arbitary, meaning mouse can be shaked towards any direction.</para>
    /// <para>THIS IMPLEMENTATION IS SUBJECT TO CHANGE</para>
    /// </summary>
    public class Shake : MouseGestureBase
    {
        /// <summary>
        /// How many times one has to shake to activate the OnSensed event.
        /// <para>Since this is detected by using a re-enterable timer without any thread sychornizing,</para>
        /// <para>This property is not always accurate. Accuracy depends on Hardware configuration</para>
        /// </summary>
        public int ExpectedRecursionCount
        {
            get
            {
                return expectedRecursionCnt;
            }
            set
            {
                if (value < 2)
                {
                    throw new InvalidOperationException("PARAMETER MUST BE MORE THAN 2");
                }
                expectedRecursionCnt = value;
            }
        }
        /// <summary>
        /// How intense in pixels one should shake the mouse in order to be regraded as finished performing one "back and forth" shake.
        /// <para>Shake intensity below this level will not result in an addition to recursion counter</para>
        /// </summary>
        public int RequiredIntensity
        {
            get
            {
                return intensityLevel;
            }
            set
            {
                intensityLevel = value;
            }
        }
        public override event EventHandler<MouseGestureEventArgBase> OnSensed;

        int intensityLevel = 1;
        int expectedRecursionCnt = 3;
        int recurCnt = -1;
        bool OrientRight = false;
        bool currOrienRight = false;
        bool OrientUp = false;
        bool currOrienUp = false;
        Win32.Point lstPst;
        Win32.Point pt;

        public Shake(Window window)
            : base(window)
        {
            SensitivityInterval = 200;
            window.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(window_MouseLeftButtonDown);
            window.PreviewMouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(window_MouseLeftButtonUp);
            tmr = new System.Timers.Timer();
            tmr.Interval = SensitivityInterval;
            tmr.Elapsed += new System.Timers.ElapsedEventHandler(tmr_Elapsed);
        }

        void window_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {

            if (fire)
            {
                if (OnSensed != null)
                {
                    wnd.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            OnSensed(null, new ShakeEventArg() { InstantFired = false });
                        }));
                }
            }
            ClrState();
        }

        void tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Win32.GetCursorPos(out pt);
            if (recurCnt == -1)
            {
                if (pt.X >= lstPst.X)
                {
                    OrientRight = true;
                }
                else
                {
                    OrientRight = false;
                }
                if (pt.Y >= lstPst.Y)
                {
                    OrientUp = true;
                }
                else
                {
                    OrientUp = false;
                }
                recurCnt = 0;
            }
            currOrienRight = lstPst.X > pt.X ? false : true;
            currOrienUp = lstPst.Y > pt.Y ? false : true;
            if ((currOrienRight != OrientRight && Math.Abs(pt.X - lstPst.X) > intensityLevel) || (currOrienUp != OrientUp && Math.Abs(pt.Y - lstPst.Y) > intensityLevel))
            {
                recurCnt++;
                if (expectedRecursionCnt <= recurCnt)
                {
                    if (InstantFire)
                    {

                        if (OnSensed != null)
                        {
                            ClrState();
                            wnd.Dispatcher.BeginInvoke(new Action(() =>
                                {
                                    OnSensed(this, new ShakeEventArg() { InstantFired = true });
                                }));
                        }
                    }
                    else
                    {
                        fire = true;
                    }
                }
            }
            OrientRight = currOrienRight;
            OrientUp = currOrienUp;
            lstPst = pt;
        }

        void ClrState()
        {
            senseState = SenseState.Idle;
            recurCnt = -1;
            OrientRight = false;
            tmr.Enabled = false;
            fire = false;
        }

        void window_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (senseState == SenseState.Idle)
            {
                senseState = SenseState.Sensing;
                Win32.GetCursorPos(out lstPst);
                tmr.Start();
            }
        }



        //public event EventHandler<ShakeEventArg> OnSensed;
    }
    enum SenseState
    {
        Sensing,
        Idle
    }
    public class ShakeEventArg : MouseGestureEventArgBase
    {
    }
}
